1
|
|
|
'use strict' |
2
|
|
|
|
3
|
|
|
let |
4
|
|
|
retry = require('p-retry'), |
5
|
|
|
Log = require('./../../core/log').Log, |
6
|
|
|
Request = require('./../../core/request').Request, |
7
|
|
|
utils = require('./../../core/utils'), |
8
|
|
|
ScriptJobBase = require('./../core/scriptjob').ScriptJob, |
9
|
|
|
Values = require('./values').Values, |
10
|
|
|
log = new Log('Platforms.CrossBrowserTesting.ScriptJob') |
11
|
|
|
|
12
|
|
|
const VARS = { |
13
|
|
|
server: 'http://hub.crossbrowsertesting.com:80/wd/hub', |
14
|
|
|
detailsUrl: 'https://crossbrowsertesting.com/api/v3/selenium?active=true&build={build}&name={name}', |
15
|
|
|
url: 'https://crossbrowsertesting.com/api/v3/selenium/{platformId}', |
16
|
|
|
username: process.env.CROSSBROWSERTESTING_USERNAME, |
17
|
|
|
accessKey: process.env.CROSSBROWSERTESTING_ACCESS_KEY |
18
|
|
|
} |
19
|
|
|
|
20
|
|
|
class ScriptJob extends ScriptJobBase { |
21
|
|
|
|
22
|
|
|
constructor(url, browser, capabilities) { |
23
|
|
|
capabilities = capabilities || { } |
24
|
|
|
utils.buildParams(capabilities) |
25
|
|
|
delete capabilities.project |
26
|
|
|
let vRet = Values.selenium(browser || { }, capabilities) |
27
|
|
|
vRet.capabilities['username'] = VARS.username |
28
|
|
|
vRet.capabilities['password'] = VARS.accessKey |
29
|
|
|
super(VARS.server, url, vRet.browser, vRet.capabilities) |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
create() { |
33
|
|
|
const iteration = () => { |
34
|
|
|
return ScriptJobBase.prototype.create.call(this) |
35
|
|
|
.catch(err => { |
36
|
|
|
if(err.message.match(/Socket timed out after/) || |
37
|
|
|
err.message.match(/A new session could not be created/)) |
38
|
|
|
{ |
39
|
|
|
throw err |
40
|
|
|
} |
41
|
|
|
throw new retry.AbortError(err.message) |
42
|
|
|
}) |
43
|
|
|
} |
44
|
|
|
return retry(iteration, { retries: 5, minTimeout: 1000, factor: 2 }) |
45
|
|
|
.then(() => { |
46
|
|
|
return getPlatformId(this) |
47
|
|
|
}) |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
markStatus(decider) { |
51
|
|
|
return ScriptJobBase.prototype.markStatus.apply(this, [ |
52
|
|
|
decider, |
53
|
|
|
VARS.url.replace(/{platformId}/, this.platformId), |
54
|
|
|
'PUT', |
55
|
|
|
markReqOptions |
56
|
|
|
]) |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
screenshot() { |
60
|
|
|
if(!this.session) { |
61
|
|
|
throw new Error('Platforms.CrossBrowserTesting.ScriptJob: session not created yet to take screenshot') |
62
|
|
|
} |
63
|
|
|
// driver's screenshot method does not work with CBT and use their |
64
|
|
|
// Selenium screenshot API needs to be used instead |
65
|
|
|
return writeRequest(VARS.url.replace(/{platformId}/, this.platformId) + '/snapshots', 'POST') |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
status() { |
69
|
|
|
return ScriptJobBase.prototype.status.apply(this, [ |
70
|
|
|
VARS.url.replace(/{platformId}/, this.platformId), |
71
|
|
|
getOptions, |
72
|
|
|
(response) => { |
73
|
|
|
if(!('active' in response) || !('state' in response)) { |
74
|
|
|
return 'stopped' |
75
|
|
|
} |
76
|
|
|
return (!response.active && 'stopped' === response.state ? 'stopped' : 'running') |
77
|
|
|
} |
78
|
|
|
]) |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
stop() { |
82
|
|
|
return ScriptJobBase.prototype.stop.call(this) |
83
|
|
|
.then(() => { |
84
|
|
|
return waitUntilStopped(this) |
85
|
|
|
}) |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
hasScreenshotOption() { |
89
|
|
|
return ('screenshots' in this.options) |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
function getPlatformId(scriptJob) { |
95
|
|
|
let url = VARS.detailsUrl |
96
|
|
|
.replace(/{name}/, scriptJob.options.name) |
97
|
|
|
.replace(/{build}/, scriptJob.options.build) |
98
|
|
|
return readRequest(url, 'GET') |
99
|
|
|
.then(response => { |
100
|
|
|
scriptJob.platformId = response.selenium[0].selenium_test_id |
101
|
|
|
log.info('platform-id %d for session %s', scriptJob.platformId, scriptJob.session) |
102
|
|
|
return Promise.resolve(true) |
103
|
|
|
}) |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
function markReqOptions(status) { |
107
|
|
|
return { |
108
|
|
|
json: true, |
109
|
|
|
resolveWithFullResponse: true, |
110
|
|
|
body: { |
111
|
|
|
action: 'set_score', |
112
|
|
|
score: 'passed' === status ? 'pass' : 'fail' |
113
|
|
|
}, |
114
|
|
|
auth: { |
115
|
|
|
user: VARS.username, |
116
|
|
|
pass: VARS.accessKey |
117
|
|
|
} |
118
|
|
|
} |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
function getOptions() { |
122
|
|
|
return { |
123
|
|
|
json: true, |
124
|
|
|
auth: { |
125
|
|
|
user: VARS.username, |
126
|
|
|
pass: VARS.accessKey |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
function waitUntilStopped(scriptJob) { |
132
|
|
|
const check = () => { |
133
|
|
|
return readRequest(VARS.url.replace(/{platformId}/, scriptJob.platformId), 'GET') |
134
|
|
|
.then(response => { |
135
|
|
|
if(!response.active && 'stopped' === response.state) { |
136
|
|
|
return true |
137
|
|
|
} |
138
|
|
|
throw new Error('not done yet') |
139
|
|
|
}) |
140
|
|
|
}, |
141
|
|
|
max = 7, minTimeout = 200, factor = 2 |
142
|
|
|
return retry(check, {retries: max, minTimeout: minTimeout, factor: factor}) |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
function readRequest(url, method) { |
146
|
|
|
let req = new Request() |
147
|
|
|
return req.request(url, method, getOptions()) |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
function writeRequest(url, method, data) { |
151
|
|
|
let req = new Request(), options = getOptions() |
152
|
|
|
options.body = data || { } |
153
|
|
|
return req.request(url, method, options) |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
exports.ScriptJob = ScriptJob |
157
|
|
|
exports.ScriptJobVars = VARS |
158
|
|
|
|